package liurong.driver.netty;

import java.util.List;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;

public class CodeTest {

	@Test
	public void server() {
		NioEventLoopGroup bosswork = new NioEventLoopGroup(1);
		NioEventLoopGroup childwork = new NioEventLoopGroup();
		try {
			ServerBootstrap serverBootstrap = new ServerBootstrap().group(bosswork, childwork)
					.channel(NioServerSocketChannel.class).childHandler(new ChannelInitializer<Channel>() {

						@Override
						protected void initChannel(Channel ch) throws Exception {
							ChannelPipeline pipeline = ch.pipeline();
							pipeline.addLast(new Encoder());
							pipeline.addLast(new Decoder());
							pipeline.addLast(new ServerHandler());
						}
					});

			ChannelFuture channelFuture = serverBootstrap.bind(8080).sync();
			channelFuture.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			bosswork.shutdownGracefully();
			childwork.shutdownGracefully();
		}
	}

	@Test
	public void client() {
		NioEventLoopGroup childwork = new NioEventLoopGroup();

		Bootstrap bootstrap = new Bootstrap();
		bootstrap.group(childwork);
		bootstrap.channel(NioSocketChannel.class);
		bootstrap.handler(new ChannelInitializer<Channel>() {

			@Override
			protected void initChannel(Channel ch) throws Exception {
				ChannelPipeline pipeline = ch.pipeline();
				pipeline.addLast(new Encoder());
				pipeline.addLast(new Decoder());
			}
		});
		try {
			ChannelFuture sync = bootstrap.connect("127.0.0.1", 8080).sync();
			Channel channel = sync.channel();

			int i = 0;
			while (true) {
				byte[] body = ("hello" + i++).getBytes();
				channel.writeAndFlush(new CustomData(body.length, body));
				TimeUnit.SECONDS.sleep(1);
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public class ServerHandler extends SimpleChannelInboundHandler<CustomData> {

		@Override
		protected void channelRead0(ChannelHandlerContext ctx, CustomData msg) throws Exception {
			System.out.println("server收到数据：length=" + msg.getBodyLength() + ", msg=" + (new String(msg.getBody())));
		}
	}

	public class Encoder extends MessageToByteEncoder<CustomData> {

		@Override
		protected void encode(ChannelHandlerContext ctx, CustomData msg, ByteBuf out) throws Exception {
			out.writeInt(msg.getBodyLength());
			out.writeBytes(msg.getBody());
		}
	}

	public class Decoder extends ByteToMessageDecoder {

		@Override
		protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
			if (in.readableBytes() < 4) {
				return;
			}
			in.markReaderIndex();
			int bodylength = in.readInt();
			if (in.readableBytes() < bodylength) {
				in.resetReaderIndex();
				return;
			}

			byte[] bytes = new byte[bodylength];
			in.readBytes(bytes);

			out.add(new CustomData(bodylength, bytes));
		}

	}

	public class CustomData {
		private int bodyLength;

		private byte[] body;

		public CustomData(int bodyLength, byte[] body) {
			super();
			this.bodyLength = bodyLength;
			this.body = body;
		}

		public int getBodyLength() {
			return bodyLength;
		}

		public void setBodyLength(int bodyLength) {
			this.bodyLength = bodyLength;
		}

		public byte[] getBody() {
			return body;
		}

		public void setBody(byte[] body) {
			this.body = body;
		}

	}
}
